home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / perl5 / Package / DeprecationManager.pm next >
Encoding:
Perl POD Document  |  2010-07-14  |  7.3 KB  |  272 lines

  1. package Package::DeprecationManager;
  2. BEGIN {
  3.   $Package::DeprecationManager::VERSION = '0.04';
  4. }
  5.  
  6. use strict;
  7. use warnings;
  8.  
  9. use Carp qw( croak );
  10. use Params::Util qw( _HASH );
  11. use Sub::Install;
  12.  
  13. sub import {
  14.     shift;
  15.     my %args = @_;
  16.  
  17.     croak
  18.         'You must provide a hash reference -deprecations parameter when importing Package::DeprecationManager'
  19.         unless $args{-deprecations} && _HASH( $args{-deprecations} );
  20.  
  21.     my %registry;
  22.  
  23.     my $import = _build_import( \%registry );
  24.     my $warn = _build_warn( \%registry, $args{-deprecations}, $args{-ignore} );
  25.  
  26.     my $caller = caller();
  27.  
  28.     Sub::Install::install_sub(
  29.         {
  30.             code => $import,
  31.             into => $caller,
  32.             as   => 'import',
  33.         }
  34.     );
  35.  
  36.     Sub::Install::install_sub(
  37.         {
  38.             code => $warn,
  39.             into => $caller,
  40.             as   => 'deprecated',
  41.         }
  42.     );
  43.  
  44.     return;
  45. }
  46.  
  47. sub _build_import {
  48.     my $registry = shift;
  49.  
  50.     return sub {
  51.         my $class = shift;
  52.         my %args  = @_;
  53.  
  54.         $args{-api_version} ||= delete $args{-compatible};
  55.  
  56.         $registry->{ caller() } = $args{-api_version}
  57.             if $args{-api_version};
  58.  
  59.         return;
  60.     };
  61. }
  62.  
  63. sub _build_warn {
  64.     my $registry      = shift;
  65.     my $deprecated_at = shift;
  66.     my $ignore        = shift;
  67.  
  68.     my %ignore = map { $_ => 1 } @{ $ignore || [] };
  69.  
  70.     my %warned;
  71.  
  72.     return sub {
  73.         my %args = @_ < 2 ? ( message => shift ) : @_;
  74.  
  75.         my ( $package, undef, undef, $sub ) = caller(1);
  76.  
  77.         my $skipped = 0;
  78.         if ( keys %ignore ) {
  79.             while ( defined $package && $ignore{$package} ) {
  80.                 # We want to start two levels back, since we already looked
  81.                 # one level back and found an internal package.
  82.                 $package = caller($skipped++ + 2);
  83.                 $skipped++;
  84.             }
  85.         }
  86.  
  87.         $package = 'unknown package' unless defined $package;
  88.  
  89.         unless ( defined $args{feature} ) {
  90.             $args{feature} = $sub;
  91.         }
  92.  
  93.         my $compat_version = $registry->{$package};
  94.  
  95.         my $deprecated_at = $deprecated_at->{ $args{feature} };
  96.  
  97.         return
  98.             if defined $compat_version
  99.                 && defined $deprecated_at
  100.                 && $compat_version lt $deprecated_at;
  101.  
  102.         my $msg;
  103.         if ( defined $args{message} ) {
  104.             $msg = $args{message};
  105.         }
  106.         else {
  107.             $msg = "$args{feature} has been deprecated";
  108.             $msg .= " since version $deprecated_at"
  109.                 if defined $deprecated_at;
  110.         }
  111.  
  112.         return if $warned{$package}{ $args{feature} }{$msg};
  113.  
  114.         $warned{$package}{ $args{feature} }{$msg} = 1;
  115.  
  116.         local $Carp::CarpLevel = $Carp::CarpLevel + 1 + $skipped;
  117.  
  118.         Carp::cluck($msg);
  119.     };
  120. }
  121.  
  122. 1;
  123.  
  124. # ABSTRACT: Manage deprecation warnings for your distribution
  125.  
  126.  
  127.  
  128. =pod
  129.  
  130. =head1 NAME
  131.  
  132. Package::DeprecationManager - Manage deprecation warnings for your distribution
  133.  
  134. =head1 VERSION
  135.  
  136. version 0.04
  137.  
  138. =head1 SYNOPSIS
  139.  
  140.   package My::Class;
  141.  
  142.   use Package::DeprecationManager -deprecations => {
  143.       'My::Class::foo' => '0.02',
  144.       'My::Class::bar' => '0.05',
  145.       'feature-X'      => '0.07',
  146.   };
  147.  
  148.   sub foo {
  149.       deprecated( 'Do not call foo!' );
  150.  
  151.       ...
  152.   }
  153.  
  154.   sub bar {
  155.       deprecated();
  156.  
  157.       ...
  158.   }
  159.  
  160.   sub baz {
  161.       my %args = @_;
  162.  
  163.       if ( $args{foo} ) {
  164.           deprecated(
  165.               message => ...,
  166.               feature => 'feature-X',
  167.           );
  168.       }
  169.   }
  170.  
  171.   package Other::Class;
  172.  
  173.   use My::Class -api_version => '0.04';
  174.  
  175.   My::Class->new()->foo(); # warns
  176.   My::Class->new()->bar(); # does not warn
  177.   My::Class->new()->far(); # does not warn again
  178.  
  179. =head1 DESCRIPTION
  180.  
  181. This module allows you to manage a set of deprecations for one or more modules.
  182.  
  183. When you import C<Package::DeprecationManager>, you must provide a set of
  184. C<-deprecations> as a hash ref. The keys are "feature" names, and the values
  185. are the version when that feature was deprecated.
  186.  
  187. In many cases, you can simply use the fully qualified name of a subroutine or
  188. method as the feature name. This works for cases where the whole subroutine is
  189. deprecated. However, the feature names can be any string. This is useful if
  190. you don't want to deprecate an entire subroutine, just a certain usage.
  191.  
  192. You can also provide an optional array reference in the C<-ignore>
  193. parameter. This is a list of package names to ignore when looking at the stack
  194. to figure out what code used the deprecated feature. This should be packages
  195. in your distribution that can appear on the call stack when a deprecated
  196. feature is used.
  197.  
  198. As part of the import process, C<Package::DeprecationManager> will export two
  199. subroutines into its caller. It proves an C<import()> sub for the caller and a
  200. C<deprecated()> sub.
  201.  
  202. The C<import()> sub allows callers of I<your> class to specify an C<-api_version>
  203. parameter. If this is supplied, then deprecation warnings are only issued for
  204. deprecations for api versions earlier than the one specified.
  205.  
  206. You must call C<deprecated()> sub in each deprecated subroutine. When called,
  207. it will issue a warning using C<Carp::cluck()>.
  208.  
  209. The C<deprecated()> sub can be called in several ways. If you do not pass any
  210. arguments, it will generate an appropriate warning message. If you pass a
  211. single argument, this is used as the warning message.
  212.  
  213. Finally, you can call it with named arguments. Currently, the only allowed
  214. names are C<message> and C<feature>. The C<feature> argument should correspond
  215. to the feature name passed in the C<-deprecations> hash.
  216.  
  217. If you don't explicitly specify a feature, the C<deprecated()> sub uses
  218. C<caller()> to identify its caller, using its fully qualified subroutine name.
  219.  
  220. A given deprecation warning is only issued once for a given package. This
  221. module tracks this based on both the feature name I<and> the error message
  222. itself. This means that if you provide severaldifferent error messages for the
  223. same feature, all of those errors will appear.
  224.  
  225. =head1 BUGS
  226.  
  227. Please report any bugs or feature requests to
  228. C<bug-package-deprecationmanager@rt.cpan.org>, or through the web interface at
  229. L<http://rt.cpan.org>.  I will be notified, and then you'll automatically be
  230. notified of progress on your bug as I make changes.
  231.  
  232. =head1 DONATIONS
  233.  
  234. If you'd like to thank me for the work I've done on this module, please
  235. consider making a "donation" to me via PayPal. I spend a lot of free time
  236. creating free software, and would appreciate any support you'd care to offer.
  237.  
  238. Please note that B<I am not suggesting that you must do this> in order
  239. for me to continue working on this particular software. I will
  240. continue to do so, inasmuch as I have in the past, for as long as it
  241. interests me.
  242.  
  243. Similarly, a donation made in this way will probably not make me work on this
  244. software much more, unless I get so many donations that I can consider working
  245. on free software full time, which seems unlikely at best.
  246.  
  247. To donate, log into PayPal and send money to autarch@urth.org or use the
  248. button on this page: L<http://www.urth.org/~autarch/fs-donation.html>
  249.  
  250. =head1 CREDITS
  251.  
  252. The idea for this functionality and some of its implementation was originally
  253. created as L<Class::MOP::Deprecated> by Goro Fuji.
  254.  
  255. =head1 AUTHOR
  256.  
  257.   Dave Rolsky <autarch@urth.org>
  258.  
  259. =head1 COPYRIGHT AND LICENSE
  260.  
  261. This software is Copyright (c) 2010 by Dave Rolsky.
  262.  
  263. This is free software, licensed under:
  264.  
  265.   The Artistic License 2.0
  266.  
  267. =cut
  268.  
  269.  
  270. __END__
  271.  
  272.